package org.piggy.client.rpc.spring;

import org.piggy.client.rpc.proxy.JdkProxy;
import org.piggy.common.anno.RemoteInvoke;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

@Component
public class ClientBeanPostProcessor implements BeanPostProcessor {

    private final static Logger logger = LoggerFactory.getLogger(ClientBeanPostProcessor.class);

    private final Map<String, Object> beanMap = new HashMap<>();

    private Object getJDKProxy(Field field) {
        return Proxy.newProxyInstance(field.getType().getClassLoader(), new Class[]{field.getType()}, new JdkProxy(field));
    }

    public Object getProxyBean(String beanName){
        return beanMap.get(beanName);
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, @NonNull String beanName) throws BeansException {
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(RemoteInvoke.class)) {
                try {
                    String name = field.getType().getName();
                    Object obj = beanMap.get(name);
                    if(obj == null){
                        obj = getJDKProxy(field);
                        beanMap.put(name, obj);
                        logger.info("create proxy bean -> " + name);
                    }
                    field.setAccessible(true);
                    field.set(bean, obj);
                }catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException {
        return bean;
    }
}
